package com.xyzwps.paimon.furina.modules.post.entity

import com.xyzwps.paimon.furina.common.DateTimeUtil
import com.xyzwps.paimon.furina.common.DeleteOption
import com.xyzwps.paimon.furina.common.DeleteOption.*
import com.xyzwps.paimon.furina.common.value.DedupKey
import org.jetbrains.exposed.sql.*
import org.springframework.stereotype.Repository

@Repository
class PostRepository {
    private val t = Post.Companion.TABLE

    fun create(
        userId: Long,
        collectionId: Long,
        title: String,
        type: PostType,
        content: String,
        tagIds: List<Long>,
        dedupKey: DedupKey
    ): Post? {
        val id = t.insertIgnoreAndGetId {
            it[t.userId] = userId
            it[t.collectionId] = collectionId
            it[t.title] = title
            it[t.type] = type
            it[t.content] = content
            it[t.tagIds] = tagIds
            it[t.dedupKey] = dedupKey.value
            it[t.createTime] = DateTimeUtil.currentLocalDateTime()
            it[t.deleteTime] = null
            it[t.updateTime] = DateTimeUtil.currentLocalDateTime()
        }
        return if (id == null) {
            findByDedupKey(userId, dedupKey)
        } else {
            findById(id.value)!!
        }
    }

    fun findByDedupKey(userId: Long, dedupKey: DedupKey, option: DeleteOption = UNDELETED_ONLY): Post? {
        val condition = when (option) {
            UNDELETED_ONLY -> Op.build { (t.dedupKey eq dedupKey.value) and (t.userId eq userId) and t.deleteTime.isNull() }
            DELETED_ONLY -> Op.build { (t.dedupKey eq dedupKey.value) and (t.userId eq userId) and t.deleteTime.isNotNull() }
            ALL -> Op.build { (t.dedupKey eq dedupKey.value) and (t.userId eq userId) }
        }
        return t.select(condition).map { Post(it) }.firstOrNull()
    }

    fun findById(id: Long, option: DeleteOption = UNDELETED_ONLY): Post? {
        val condition = when (option) {
            UNDELETED_ONLY -> Op.build { (t.id eq id) and t.deleteTime.isNull() }
            DELETED_ONLY -> Op.build { (t.id eq id) and t.deleteTime.isNotNull() }
            ALL -> Op.build { t.id eq id }
        }
        return t.select(condition).map { Post(it) }.firstOrNull()
    }

    fun findByCollectionId(collectionId: Long, option: DeleteOption = UNDELETED_ONLY): List<Post> {
        val condition = when (option) {
            UNDELETED_ONLY -> Op.build { (t.collectionId eq collectionId) and t.deleteTime.isNull() }
            DELETED_ONLY -> Op.build { (t.collectionId eq collectionId) and t.deleteTime.isNotNull() }
            ALL -> Op.build { t.collectionId eq collectionId }
        }
        return t.select(condition)
            .orderBy(t.id to SortOrder.DESC)
            .map { Post(it) }
    }

    fun update(post: Post) {
        t.update({ t.id eq post.id }) {
            it[t.collectionId] = post.collectionId
            it[t.title] = post.title
            it[t.type] = post.type
            it[t.content] = post.content
            it[t.deleteTime] = post.deleteTime
            it[t.updateTime] = post.updateTime
        }
    }

    fun deleteByCollectionId(collectionId: Long, userId: Long) {
        val now = DateTimeUtil.currentLocalDateTime()
        t.update({ (t.collectionId eq collectionId) and (t.userId eq userId) and t.deleteTime.isNull() }) {
            it[t.deleteTime] = now
            it[t.updateTime] = now
        }
    }
}